package ace.tool.generate.code.impl.manager.common;

import ace.cmp.util.CaseFormatUtils;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.GenerateContext;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.ApiClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.DtoClassFieldInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.DtoClassFieldRemarkMetaInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.DtoClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.api.EnumClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.AggregateClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.EntityClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.GraphqlAdminControllerClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.ManagerClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.RepositoryClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.RestAdminControllerClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.resources.GraphqlDtoFileInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.resources.GraphqlInputFileInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.resources.model.dto.LiquibaseTemplateModelField;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.core.test.TestGraphqlTestFileBundle;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.sdk.FeignApiClassInfo;
import ace.tool.generate.code.impl.resolver.scheme.model.bo.sdk.FeignClassInfo;
import ace.tool.generate.code.impl.resolver.ui.model.bo.ResponseClassInfo;
import ace.tool.generate.code.impl.resolver.ui.model.bo.UiServerApiClassInfo;
import ace.tool.generate.code.impl.service.model.input.GenerateInput;
import ace.tool.generate.code.impl.service.model.input.GenerateModelFieldInput;
import ace.tool.generate.code.impl.service.model.input.GenerateModelInput;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;

/**
 * @author caspar
 * @date 2024/1/18 10:57
 */
public class EntityModelManager {

  private static final EntityModelManager INSTANCE = new EntityModelManager();

  public static EntityModelManager getInstance() {
    return INSTANCE;
  }

  public DtoClassInfo getDtoClassInfoByEntityModelId(
      GenerateContext context, String entityModelId) {
    return context.getModuleProject().getApiProject().getDtoClasses().stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public ApiClassInfo getApiClassInfoByEntityModelId(
      GenerateContext context, String entityModelId) {
    return this.getApiClassInfoByEntityModelId(context.getModuleProject().getApiProject().getApiClasses(), entityModelId);
  }

  public ApiClassInfo getApiClassInfoByEntityModelId(
      List<ApiClassInfo> apiClassInfos, String entityModelId) {
    return apiClassInfos.stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public EntityClassInfo getEntityClassInfoByEntityModelId(
      GenerateContext context, String entityModelId) {
    return this.getEntityClassInfoByEntityModelId(
        context.getModuleProject().getCoreProject().getEntityClassInfos(), entityModelId);
  }

  public EntityClassInfo getEntityClassInfoByEntityModelId(
      List<EntityClassInfo> entityClassInfos, String entityModelId) {
    return entityClassInfos.stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public ManagerClassInfo getManagerClassInfoByEntityModelId(
      GenerateContext context, String entityModelId) {
    return this.getManagerClassInfoByEntityModelId(context.getModuleProject().getCoreProject().getManagerClassInfos(), entityModelId);
  }

  public ManagerClassInfo getManagerClassInfoByEntityModelId(
      List<ManagerClassInfo> managerClassInfos, String entityModelId) {
    return managerClassInfos.stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public FeignApiClassInfo getFeignApiClassInfoByEntityModelId(
      GenerateContext context, String entityModelId) {
    return context.getModuleProject().getSdkProject().getFeignApiClassInfos().stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public FeignClassInfo getFeignClassInfoByEntityModelId(
      GenerateContext context, String entityModelId) {
    return context.getModuleProject().getSdkProject().getFeignClassInfos().stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public String getEntityModelId(GenerateInput input, GenerateModelInput modelInput) {
    return modelInput.getName();
  }

  public String getEnumId(GenerateModelInput modelInput, GenerateModelFieldInput modelFieldInput) {
    return modelInput.getName() + modelFieldInput.getName();
  }

  public Optional<EnumClassInfo> getEnumClassInfoByEnumId(
      List<EnumClassInfo> enumClassInfos, String enumId) {
    return enumClassInfos.stream().filter(p -> p.getEnumId().equals(enumId)).findFirst();
  }

  public Optional<EnumClassInfo> getEnumClassInfoBy(
      GenerateModelInput modelInput,
      GenerateModelFieldInput modelFieldInput,
      List<EnumClassInfo> enumClassInfos) {
    String enumId = this.getEnumId(modelInput, modelFieldInput);
    return this.getEnumClassInfoByEnumId(enumClassInfos, enumId);
  }

  public GraphqlDtoFileInfo getGraphqlDtoFileInfo(
      List<GraphqlDtoFileInfo> graphqlDtoFileInfos, String entityModelId) {
    return graphqlDtoFileInfos.stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public GraphqlInputFileInfo getGraphqlInputFileInfo(
      List<GraphqlInputFileInfo> graphqlInputFileInfos, String entityModelId) {
    return graphqlInputFileInfos.stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public GraphqlAdminControllerClassInfo getGraphqlAdminControllerClassInfo(
      List<GraphqlAdminControllerClassInfo> graphqlAdminControllerClassInfos,
      String entityModelId) {
    return graphqlAdminControllerClassInfos.stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public RepositoryClassInfo getRepositoryByEntityModelId(
      List<RepositoryClassInfo> repositoryClassInfos, String entityModelId) {
    return repositoryClassInfos.stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public TestGraphqlTestFileBundle getTestGraphqlTestFileBundleByEntityModelId(
      GenerateContext context, String entityModelId) {
    return context.getModuleProject().getCoreProject().getTestGraphqlTestFileBundles().stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public RestAdminControllerClassInfo getRestAdminControllerClassInfoByEntityModelId(
      GenerateContext context, String entityModelId) {
    return context.getModuleProject().getCoreProject().getRestAdminControllerClassInfos().stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public String getModelFieldEnumRemarkNotes(GenerateModelFieldInput modelFieldInput) {
    StringBuilder stringBuilder = new StringBuilder();
    if (StringUtils.isNotEmpty(modelFieldInput.getRemarkNotes())) {
      stringBuilder.append(modelFieldInput.getRemarkNotes());
    }
    if (modelFieldInput.getEnums() != null) {

      String remarkNotes = modelFieldInput.getEnums()
          .getFields()
          .stream()
          .map(enumClassFieldInfo -> String.format("%s-%s", enumClassFieldInfo.getCode(), enumClassFieldInfo.getDesc()))
          .collect(Collectors.joining(" "));
      if (StringUtils.isNotEmpty(remarkNotes)) {
        stringBuilder.append(" ");
        stringBuilder.append(remarkNotes);
      }
    }
    return stringBuilder.toString();
  }

  public String getStringSizeValid(GenerateModelFieldInput modelFieldInput) {
    if (StringUtils.isNotBlank(modelFieldInput.getDbType())) {
      return null;
    }
    if (!modelFieldInput.getType().equals(String.class)) {
      return null;
    }
    return modelFieldInput.getMaxLength().toString();
  }

  public String getModelFieldJavaComment(
      GenerateModelFieldInput modelFieldInput, Optional<EnumClassInfo> enumInfoOptional) {
    Map<String, String> data = new LinkedHashMap<>();
    if (StringUtils.isNotEmpty(modelFieldInput.getJavaComment())) {
      data.put("javaComment", modelFieldInput.getJavaComment());
    }
    if (enumInfoOptional.isPresent()) {
      String enumRemark =
          String.format("{@link %s}", enumInfoOptional.get().getInfo().getClassFullName());
      data.put("enumRemark", enumRemark);
    }
    if (data.size() == 0) {
      return null;
    }
    return data.entrySet().stream().map(p -> p.getValue()).collect(Collectors.joining(" "));
  }

  public AggregateClassInfo getAggregateClassInfoByEntityModelId(
      List<AggregateClassInfo> aggregateClassInfos, String entityModelId) {
    return aggregateClassInfos.stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public ResponseClassInfo getUiResponseClassInfoByEntityId(
      List<ResponseClassInfo> responseClassInfos, String entityModelId) {
    return responseClassInfos.stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public UiServerApiClassInfo getUiServerApiClassInfoByEntityId(
      List<UiServerApiClassInfo> uiServerApiClassInfos, String entityModelId) {
    return uiServerApiClassInfos.stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .get();
  }

  public DtoClassFieldRemarkMetaInfo getDtoClassFieldRemarkMetaInfoBy(String entityModelId, String fieldName, List<DtoClassInfo> dtoClassInfos) {
    return dtoClassInfos.stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .map(DtoClassInfo::getFieldInfos)
        .stream()
        .flatMap(List::stream)
        .filter(p -> p.getName().equals(fieldName))
        .findFirst()
        .map(DtoClassFieldInfo::getRemarkMeta)
        .orElse(null);
  }

  public DtoClassInfo getDtoClassInfoByEntityModelId(String entityModelId, List<DtoClassInfo> dtoClasses) {
    return dtoClasses.stream()
        .filter(p -> p.getEntityModelId().equals(entityModelId))
        .findFirst()
        .orElse(null);
  }

  public DtoClassInfo getDtoClassInfoByEntityModelId(List<DtoClassInfo> dtoClasses, String entityModelId) {
    return this.getDtoClassInfoByEntityModelId(entityModelId, dtoClasses);
  }

  public LiquibaseTemplateModelField getTableFieldByFieldName(List<LiquibaseTemplateModelField> fields, String fieldName) {
    String columnName = CaseFormatUtils.convertLowerCamelToUpperUnderscore(fieldName);
    return this.getTableFieldByColumnName(fields, columnName);
  }

  public LiquibaseTemplateModelField getTableFieldByColumnName(List<LiquibaseTemplateModelField> fields, String columnName) {
    return Stream.ofNullable(fields)
        .flatMap(List::stream)
        .filter(p -> p.getName().equals(columnName))
        .findFirst()
        .orElse(null);
  }

  public String convertFieldNameToColumnName(String fieldName) {
    return CaseFormatUtils.convertLowerCamelToLowerUnderscore(fieldName);
  }

  public String convertClassNameToTableName(String className) {
    return CaseFormatUtils.convertUpperCamelToLowerUnderscore(className);
  }
}
